package com.onlyxiahui.common.utils.base.compare;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.onlyxiahui.common.utils.base.compare.DifferentValueUtil.Different;
import com.onlyxiahui.common.utils.base.lang.reflect.ReflectUtil;

/**
 * 
 * Description <br>
 * Date 2020-11-10 20:57:04<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
public class DifferentValueUtil {

	/**
	 * Description 2个属性值判断是否相等<br>
	 * 1、当2个值都为null->true<br>
	 * 2、当2个值都不为null并且相等->true<br>
	 * 3、当2个值都不为null并且不相等->false<br>
	 * 4、当2个值的其中一个为null->false<br>
	 * 5、当2个属性oldSkipNull/newSkipNull设置为true，那么设置为true的属性值为null->true<br>
	 * 
	 * Date 2020-11-10 16:03:31<br>
	 * 
	 * @param oldValue
	 * @param oldFieldName
	 * @param oldSkipNull
	 * @param newValue
	 * @param newFieldName
	 * @param newSkipNull
	 * @return
	 * @since 1.0.0
	 */
	public static Info isEquals(
			Object oldValue,
			String oldFieldName,
			boolean oldSkipNull,
			Object newValue,
			String newFieldName,
			boolean newSkipNull) {

		boolean isEquals = false;
		Info info = null;
		if (oldSkipNull && null == oldValue) {
			isEquals = true;
		} else if (newSkipNull && null == newValue) {
			isEquals = true;
		} else {
			isEquals = EqualsUtil.isEquals(oldValue, newValue);
		}
		if (!isEquals) {
			info = new Info();
			info.oldName = oldFieldName;
			info.newName = newFieldName;
			info.newValue = newValue;
			info.oldValue = oldValue;
		}
		return info;
	}

	/**
	 * Description 2个属性值判断是否相等<br>
	 * 1、当2个属性的值或者属性都为null->true<br>
	 * 2、当2个属性的值或者属性都不为null并且相等->true<br>
	 * 3、当2个属性的值或者属性都不为null并且不相等->false<br>
	 * 4、当2个属性的值或者属性都其中一个为null->false<br>
	 * 5、当2个属性oldSkipNull/newSkipNull设置为true，那么设置为true的属性值为null->true<br>
	 * <br>
	 * Date 2020-11-10 16:04:29<br>
	 * 
	 * @param oldData
	 * @param oldFieldName
	 * @param oldField
	 * @param oldSkipNull  :true:值为空的时候跳过，认为相等；false:必须比较是否相等
	 * @param newData
	 * @param newFieldName
	 * @param newField
	 * @param newSkipNull  :true:值为空的时候跳过，认为相等；false:必须比较是否相等
	 * @return
	 * @since 1.0.0
	 */
	public static Info isEquals(
			Object oldData,
			String oldFieldName,
			Field oldField,
			boolean oldSkipNull,
			Object newData,
			String newFieldName,
			Field newField,
			boolean newSkipNull) {
		boolean isEquals = false;
		Info info = null;

		Object oldValue = null;
		Object newValue = null;

		if (null != oldField) {
			oldValue = ReflectUtil.getFieldValue(oldData, oldField);
		}

		if (null != newField) {
			newValue = ReflectUtil.getFieldValue(newData, newField);
		}

		if (oldSkipNull && null == oldValue) {
			isEquals = true;
		} else if (newSkipNull && null == newValue) {
			isEquals = true;
		} else {
			isEquals = EqualsUtil.isEquals(oldValue, newValue);
		}
		if (!isEquals) {
			info = new Info();
			info.oldName = oldFieldName;
			info.newName = newFieldName;
			info.newValue = newValue;
			info.oldValue = oldValue;
		}
		return info;
	}

	/**
	 * Description 2个对象进行比较，返回值发生变化的信息列表<br>
	 * Date 2020-11-10 16:13:26<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyMap    ：这个map用来存2个对象的属性名称，如oldData中的name需要和newData中的username比较，那么propertyMap.put("name","username");
	 * @param oldData
	 * @param oldRootClass
	 * @param oldSkipNullMap ：这个map用来指定属性为null的时候是否跳过，true跳过false不跳过，oldSkipNullMap.put("name",true);
	 * @param newData
	 * @param newRootClass
	 * @param newSkipNullMap
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> List<Different> different(
			Map<String, String> propertyMap,
			T oldData,
			Class<?> oldRootClass,
			Map<String, Boolean> oldSkipNullMap,
			E newData,
			Class<?> newRootClass,
			Map<String, Boolean> newSkipNullMap) {
		List<Different> list = new ArrayList<>();

		// 各参数都不能为空
		if (null != oldData && null != newData) {

			oldRootClass = null == oldRootClass ? Object.class : oldRootClass;
			newRootClass = null == newRootClass ? Object.class : newRootClass;

			Class<?> oldClass = oldData.getClass();
			Class<?> newClass = newData.getClass();

			boolean isOldMap = (oldData instanceof Map);
			boolean isNewMap = (newData instanceof Map);

			if (null == propertyMap) {
				propertyMap = new HashMap<>(128);

				if (isNewMap) {
					Map<?, ?> map = (Map<?, ?>) newData;

					for (Object key : map.keySet()) {
						if (null != key) {
							String name = key.toString();
							propertyMap.put(name, name);
						}
					}
				} else {
					List<Field> fields = ReflectUtil.getFieldList(newClass, newRootClass);
					for (Field f : fields) {
						propertyMap.put(f.getName(), f.getName());
					}
				}
			}

			Set<String> keySet = propertyMap.keySet();
			for (String oldFieldName : keySet) {
				// 取出需要对比的属性名
				String newFieldName = propertyMap.get(oldFieldName);
				newFieldName = (null == newFieldName) ? oldFieldName : newFieldName;

				Object oldValue = null;
				Object newValue = null;

				if (isOldMap) {
					oldValue = ((Map<?, ?>) oldData).get(oldFieldName);
				} else {
					Field oldField = ReflectUtil.getField(oldClass, oldRootClass, oldFieldName);
					oldValue = ReflectUtil.getFieldValue(oldData, oldField);
				}

				if (isNewMap) {
					newValue = ((Map<?, ?>) newData).get(newFieldName);
				} else {
					Field newField = ReflectUtil.getField(newClass, newRootClass, newFieldName);
					newValue = ReflectUtil.getFieldValue(newData, newField);
				}

				boolean oldSkipNull = (null != oldSkipNullMap && null != oldSkipNullMap.get(oldFieldName)) ? oldSkipNullMap.get(oldFieldName) : false;
				boolean newSkipNull = (null != newSkipNullMap && null != newSkipNullMap.get(newFieldName)) ? newSkipNullMap.get(newFieldName) : false;
				Info info = isEquals(
						oldValue,
						oldFieldName,
						oldSkipNull,
						newValue,
						newFieldName,
						newSkipNull);
				if (null != info) {
					list.add(info);
				}
			}
		}
		return list;
	}

	/************************************************************************************************************/

	public static <T, E> List<Different> different(
			Map<String, String> propertyMap,
			T oldData, Class<?> oldRootClass,
			boolean oldSkipNull,
			E newData, Class<?> newRootClass,
			boolean newSkipNull) {

		Map<String, Boolean> oldSkipNullMap = null;
		Map<String, Boolean> newSkipNullMap = null;

		if (null != propertyMap) {
			int size = propertyMap.size();
			oldSkipNullMap = new HashMap<>(size);
			newSkipNullMap = new HashMap<>(size);
			Set<String> keySet = propertyMap.keySet();
			for (String oldFieldName : keySet) {
				// 取出需要对比的属性名
				String newFieldName = propertyMap.get(oldFieldName);
				newFieldName = (null == newFieldName) ? oldFieldName : newFieldName;

				oldSkipNullMap.put(oldFieldName, oldSkipNull);
				newSkipNullMap.put(newFieldName, newSkipNull);
			}
		} else {
			if (null != oldData) {
				List<Field> oldFields = ReflectUtil.getFieldList(oldData.getClass(), oldRootClass);
				oldSkipNullMap = new HashMap<>(oldFields.size());
				for (Field f : oldFields) {
					oldSkipNullMap.put(f.getName(), oldSkipNull);
				}
			}
			if (null != newData) {
				List<Field> newFields = ReflectUtil.getFieldList(newData.getClass(), newRootClass);
				newSkipNullMap = new HashMap<>(newFields.size());
				for (Field f : newFields) {
					newSkipNullMap.put(f.getName(), newSkipNull);
				}
			}
		}
		return different(propertyMap, oldData, oldRootClass, oldSkipNullMap, newData, newRootClass, newSkipNullMap);
	}

	public static <T, E> List<Different> different(
			Map<String, String> propertyMap,
			T oldData,
			Class<?> oldRootClass,
			E newData,
			Class<?> newRootClass) {
		return different(propertyMap, oldData, oldRootClass, false, newData, newRootClass, false);
	}

	public static <T, E> List<Different> different(
			Map<String, String> propertyMap,
			T oldData,
			boolean oldSkipNull,
			E newData,
			boolean newSkipNull) {
		return different(propertyMap, oldData, null, oldSkipNull, newData, null, newSkipNull);
	}

	public static <T, E> List<Different> different(Map<String, String> propertyMap, T oldData, E newData) {
		return different(propertyMap, oldData, null, false, newData, null, false);
	}

	public static <T, E> List<Different> different(T oldData, E newData) {
		return different((Map<String, String>) null, oldData, newData);
	}

	/************************************************************************************************************/
	/**
	 * Description 对比2个对象不同值的属性 Date 2020-11-10 16:18:47<br>
	 * 
	 * @param <T>
	 * @param <E>
	 * @param propertyList
	 * @param oldData
	 * @param newData
	 * @return
	 * @since 1.0.0
	 */
	public static <T, E> List<Different> different(List<String> propertyList, T oldData, E newData) {
		return different(propertyList, oldData, false, newData, false);
	}

	public static <T, E> List<Different> different(List<String> propertyList, T oldData, boolean oldSkipNull, E newData, boolean newSkipNull) {
		return different(propertyList, oldData, null, oldSkipNull, newData, null, newSkipNull);
	}

	public static <T, E> List<Different> different(List<String> propertyList, T oldData, Class<?> oldRootClass, boolean oldSkipNull, E newData, Class<?> newRootClass,
			boolean newSkipNull) {
		Map<String, String> propertyMap = null;
		// 各参数不能为空
		if (null != propertyList) {
			propertyMap = new HashMap<>(propertyList.size());
			for (String name : propertyList) {
				propertyMap.put(name, name);
			}
		}
		return different(propertyMap, oldData, oldRootClass, oldSkipNull, newData, newRootClass, newSkipNull);
	}

	/**
	 * 
	 * Description 不同值属性相关信息<br>
	 * Date 2020-11-10 16:01:37<br>
	 * 
	 * @author XiaHui [onlovexiahui@qq.com]<br>
	 * @since 1.0.0
	 */
	public interface Different {

		/**
		 * 
		 * <br>
		 *
		 * @date 2022-05-24 12:11:42<br>
		 * @return
		 * @since 1.0.0
		 */
		public String getNewName();

		/**
		 * 
		 * <br>
		 *
		 * @date 2022-05-24 12:11:46<br>
		 * @return
		 * @since 1.0.0
		 */
		public String getOldName();

		/**
		 * 
		 * 属性新值<br>
		 *
		 * @date 2022-05-24 12:15:19<br>
		 * @return
		 * @since 1.0.0
		 */
		public Object getNewValue();

		/**
		 * 
		 * 属性原来的值<br>
		 *
		 * @date 2022-05-24 12:15:30<br>
		 * @return
		 * @since 1.0.0
		 */
		public Object getOldValue();
	}
}

class Info implements Different {

	String newName;
	String oldName;
	Object newValue;
	Object oldValue;

	@Override
	public String getNewName() {
		return newName;
	}

	public void setNewName(String newName) {
		this.newName = newName;
	}

	@Override
	public String getOldName() {
		return oldName;
	}

	public void setOldName(String oldName) {
		this.oldName = oldName;
	}

	@Override
	public Object getNewValue() {
		return newValue;
	}

	public void setNewValue(Object newValue) {
		this.newValue = newValue;
	}

	@Override
	public Object getOldValue() {
		return oldValue;
	}

	public void setOldValue(Object oldValue) {
		this.oldValue = oldValue;
	}
}